<?php
	require_once(dirname(__FILE__)."/incluir.php");
	require_once(dirname(__FILE__)."/dados.php");
	
	// Classe que manipula um viale genérico
	class Model
	{
		// Informações da tabela do banco de dados
		public $tabela; // A tabela que no BD que essa entidade representará
		public $nome_entidade; // O nome real que representa essa entidade
		protected $id; // Se a classe representar apenas uma linha da tabela, esse será o seu id

		public $fields; // Guarda todos os nomes dos campos das tabelas
		
		protected $types; // Guarda os tipos dos campos no BD (exemplo: varchar, int, etc..),
		// não sendo necessário visualizar esse array fora dessa classe
		
		public $campos; // Usa o array $fields e o array $types para criar um array de instancias da classe Campo. 
		//Esse array terá dados detalhados dos campos como qual é a sua validação e qual seria o html que representaria melhor o campo
		
		// Um array com as informações de quais campos serão exibidos em qual caso de uso
		protected $campos_paginas;
		
		// Cada campo terá um sufixo para poder diferenciá-lo caso tenha campos de duas entidades em algum caso de uso
		public $sufixo;
		
		// Classes que auxiliam a classe Viale 
		public $sqlController;
		
		// Os dados públicos que serão usados nos casos de uso 
		public $dados; // Os valores dos campos da tabela que a entidade representa
		
		// Dados de relacionamento
		protected $tabelas_excluir; // Em quais tabelas também devemos excluir essa entidade 
		protected $campos_valores_default; // Os valores Default dos campos
		public $campos_multiplos; // Quais campos são campos multiplos
		
		// O construtor permite que você escolha passar a sua própria classe de banco de dados.
		// O contrutor também permite que você escolha os campos que serão exibidos em cada caso de uso.
		public function __construct($id = null, $tabela_valor = null,
							$sufixo_valor = null, $nome_entidade_valor = null,
							$campos_paginas_valor = null, $campos_valores_default_valor = null, 
							$campos_multiplos = null, $tabelas_excluir_valor = array(),
							$campos_checkbox_valor, $campos_radio_valor, $campos_select_valor,
							$campos_valor = null,
							&$sqlController = null)
		{
			require(dirname(__FILE__)."/dados.php"); // Em ultimo caso procura em dados.php que está nesse diretorio os valores dos campos
						
			$this->tabela = ($tabela_valor == null) ? $tabela : $tabela_valor;
			$this->nome_entidade = ($nome_entidade_valor == null) ? $nome_entidade : $nome_entidade_valor;
			$this->sufixo = ($sufixo_valor == null) ? $sufixo : $sufixo_valor;
			
			$this->campos_paginas = ($campos_paginas_valor == null) ? $campos_paginas : $campos_paginas_valor;
			$this->campos_multiplos = ($campos_multiplos_valor == null) ? $campos_multiplos : $campos_multiplos_valor;
			$this->campos_valores_default = ($campos_valores_default_valor == null) ? $campos_valores_default : $campos_valores_default_valor;
			
			$this->tabelas_excluir = (empty($tabelas_excluir_valor)) ? $tabelas_excluir : $tabelas_excluir_valor;
			
			$this->dados = array();

			if($sqlController == null) // se não foi passado uma instancia de um controlador de banco de dados, usa o de mysql
			{
				$this->sqlController = new MySQLController($this->tabela); // Crio um controlador que manipula a tabela passada
			}
			else
			{
				$this->sqlController = $sqlController;
			}
			
			$this->carrega_campos_BD(); // Pego as informações para os atributos $fields e $types
			
			$this->id = ($id == null) ? $this->get['id'] : $id; // Se id for null pega o id do get, se não é para usar o id passado
			if($this->id != null) // se essa entidade realmente deve ser uma linha da tabela, carrega os dados dessa linha no array dados
			{
				$this->dados = $this->get_row($this->id);
			}
			
			// Se nao foi passado o array de campos, gera esse array
			if($campos_valor == null)
			{
				$this->get_campos($campos_checkbox_valor, $campos_radio_valor, $campos_select_valor);
			}
			else
			{
				$this->campos = $campos_valor;
			}
			
		}
		
		// Funções auxiliares (funções privadas ou protecteds)
		
		// Pega do BD as informações para os atributos $fields e $types
		protected function carrega_campos_BD()
		{
			$this->fields = array();
			$this->types = array();
			$this->sqlController->load_fields($this->fields, $this->types);
		}
		
		// Os valores de retorno são: data, password, telefone, text, textarea
		protected function get_tipo_campo($nome, $tipo, $campos_checkbox, $campos_radio, $campos_select)
		{
			// Checagem pelo nome
			if(stristr($nome, 'telefone') != false)
			{
				 return 'telefone';
			}
			else if(stristr($nome, 'senha') != false)
			{
				 return 'password';
			}
			else if(stristr($nome, '_id') != false)
			{
				 return 'select';
			}
			// Checagem pelo tipo
			if(stristr($tipo, 'varchar') != false)
			{
				return 'text';
			}
			else if(stristr($tipo, 'text') != false)
			{
				return 'textarea';
			}
			else if(stristr($tipo, 'date') != false)
			{
				return 'data';
			}
			else if(array_key_exists($nome, $campos_select))
			{
				return 'select';
			}
			else if(array_key_exists($nome, $campos_checkbox))
			{
				return 'checkbox';
			}
			else if(array_key_exists($nome, $campos_radio))
			{
				return 'radio';
			}
			return 'text';
		}
		
		// Função auxiliar que irá retornar se tiver, os valores do html dos campos 
		protected function get_valores_campo($nome, $tipo_campo, $campos_checkbox, $campos_radio, $campos_select)
		{
			$campos = array(); // Dependendo do tipo valerá um dos arrays passados
			switch($tipo_campo)
			{
				case 'select':
					$campos = $campos_select;
					break;
				case 'checkbox':
					$campos = $campos_checkbox;
					break;
				case 'radio':
					$campos = $campos_radio;
					break;
				default:
					break;
			}
			if(is_array($campos))
				return $campos[$nome]; // Retorna o valor do array
			return null; // Se não tem o array de campos, retorna null indicando isso 
		}
		
		protected function get_tabela_campo($field, $tipo_campo)
		{
			$final_nome = substr($field, -4); // Pega os últimos 3 caracteres do nome do campo
			
			// Retorna a tabela de onde pegar os dados, se o nome terminar com _id, é porque é outra tabela
			if($tipo_campo == 'select' && $final_nome == '_id')
			{
				// Como é um select, pegará as informações de outra tabela
				$tabela = str_replace('_id', '', $field); // se for por exemplo gerente_id, $tabela ficará com o valor gerente
				return $tabela;
			}
			return $this->tabela;
		}
		
		// Função que preenche o array campos automaticamente com informações para todo o viale
		protected function get_campos($campos_checkbox, $campos_radio, $campos_select)
		{
			$this->campos = array();
			// Percorre o array fields para montar o array campos
			foreach($this->fields as $field)
			{
				// Pega o tipo do campo para poder ver a sua validação e poder contruir o seu html
				$type = $this->types[$field];
				$validacoes = Validacao::acha_validacao($field, $this->sufixo, $type, $this->tabela);
				
				// O tipo no banco de dados e para o html são diferentes, essa função faz a converte do tipo no banco para o tipo no html
				$tipo_campo = $this->get_tipo_campo($field, $type, $campos_checkbox, $campos_radio, $campos_select);
				$valores_campo = null; // Vai valer uma lista de valores, se for para exibir um select, ou vários checkboxes, ou vários radio button
				if($tipo_campo == 'select' || $tipo_campo == 'checkbox' || $tipo_campo == 'radio') // Se for para passar valores pré estabelecidos
				{
					$valores_campo = $this->get_valores_campo($field, $tipo_campo, $campos_checkbox, $campos_radio, $campos_select); // Pega os valores a serem exibidos
				}
				
				$valor_default = (isset($this->campos_valores_default[$field])) ? $this->campos_valores_default[$field] : '';
				// Se não for um email escreverá a label do campo como sendo o nome do campo com a primeira letra maiúscula
				$label = (stristr($field, 'email') != false) ? 'E-mail' : ucfirst($field);

				// Cria o campo
				$campo = new Campo($field, $this->sufixo, $tipo_campo, $label, $valor_default,
				 		isset($this->campos_multiplos[$field]), $this->campos_paginas[$field], $validacoes, 
				 		$this->get_tabela_campo($field, $tipo_campo), // Se o tipo for um select o campo pega as informações de outra tabela
				 		$valores_campo
				 		);
				$this->campos[$field] = $campo;
				
				// Se for uma senha ou um email será necessário um campo extra para confirmar os dados
				if(stristr($field, 'email') != false)
				{
					$field_campo_confirma = 'confirma_'.$field;
					// cria o campo extra que servirá para confirmar o email
					$validacoes_confirma = Validacao::acha_validacao($field_campo_confirma, $this->sufixo, $type, $this->tabela, $field);
					$campo_confirma = new Campo($field_campo_confirma, $this->sufixo, $tipo_campo, 'Confirma E-mail', $valor_default,
									 $this->campos_multiplos[$field], $this->campos_paginas[$field], $validacoes_confirma, $this->tabela, $valores_campo);
					$this->campos[$field_campo_confirma] = $campo_confirma; // a chave é o nome do campo e o valor uma instancia de campo
				}
				else if(stristr($field, 'senha') != false)
				{
					$field_campo_confirma = 'confirma_'.$field;
					// cria o campo extra que servirá para confirmar o email
					$validacoes_confirma = Validacao::acha_validacao($field_campo_confirma, $this->sufixo, $type, $this->tabela, $field);
					$campo_confirma = new Campo($field_campo_confirma, $this->sufixo, $tipo_campo, 'Confirma Senha', $valor_default,
									 $this->campos_multiplos[$field], $this->campos_paginas[$field], $validacoes_confirma, $this->tabela, $valores_campo);
					$this->campos[$field_campo_confirma] = $campo_confirma;
				}
			}
		}
		
		// remove todos os valores do post, que pertencem apenas a essa entidade
		protected function remove_valores_post()
		{
			foreach($this->fields as $field)
			{
				unset($this->post[$field.$this->sufixo]);
			}
		}

		// Funções públicas
		
		// Pega todos os valores da tabela do viale
		public function get_all($fields = null, $tabela = null, $where = null, $orderBy = null, $order = null, $limit = null, $offset = null)
		{
			return $this->sqlController->get_rows($fields, $tabela, $where, $orderBy, $order, $limit, $offset);
		}
		
		// Pega um valor específico da tabela, dependendo do valor da variável $id passado, ou do id passado por get
		public function get_row($id = null, $fields = null, $tabela = null, $where = null)
		{
			$id = ($id == null) ? $this->id : $id; // Se id for null pega o id do get, se não é para usar o id passado  
			return $this->sqlController->get_row($id, $fields, $tabela, $where);
		}
		
		// Pega apenas o valor de um campo de apenas uma linha
		public function get_element($element = null, $id = null, $tabela = null, $where = null)
		{
			$id = ($id == null) ? $this->id : $id; // Se id for null pega o id do get, se não é para usar o id passado  
			return $this->sqlController->get_element($element, $id, $tabela, $where);
		}
		
		// Pega apenas o valor de um campo das linhas retornadas, tudo em um array só
		public function get_elements($element = null, $tabela = null, $where = null, $orderBy = null, $order = null, $limit = null, $offset = null)
		{
			return $this->sqlController->get_elements($element, $tabela, $where, $orderBy, $order, $limit, $offset);
		}
		
		// Pega a representação json do objeto
		public function get_json()
		{
			return json_encode($this->dados, JSON_FORCE_OBJECT);
		}
		
		// Pega o ultimo id da entidade
		public function get_last_id()
		{
			return $this->sqlController->last_id_inserted();
		}
		
		public function get_campo_instance($nome)
		{
			return $this->campos[$nome];
		}
		
		// Função que fará o caso de uso inserir ou o caso de uso altera (bem parecidos), dependendo dos valores passados 
		protected function inserir_alterar($funcionalidade, $dados = null)
		{
			$this->dados = isset($dados) ? $dados : $this->dados; // Coloca os ultimos dados inseridos ou alterados como os dados do Model
			
			// Dependendo da funcionalidade passada (inserir ou alterar), executa a função que insere ou altera a linha do BD
			// Se inseriu/alterou corretamente, retornará true, caso contrário retornará false.
			if($funcionalidade == "inserir")
			{
				if(!$this->sqlController->insert_row($this->dados, $this->fields, $this->campos))
				{
					return false;
				}
			}
			else
			{
				if(!$this->sqlController->update_row($this->id, $this->dados, $this->fields, $this->campos))
				{
					return false;
				}
			}
			return true;
		}
		
		// Funções do Viale

		// Insere uma linha da entidade no banco de dados
		public function inserir($dados = null)
		{
			return $this->inserir_alterar('inserir', $dados);
		}
		
		// Altera uma linha da entidade no banco de dados, caso um id seja passado ele será usado para isso
		public function alterar($id = null, $dados = null)
		{
			$this->id = ($id == null) ? $this->id : $id; // decide qual id usar, o que já tinha ou o passado por parametro
			return $this->inserir_alterar('alterar', $dados);
		}
		
		// Adaptado para funcionar com Model
		public function excluir_relacionamentos(&$session, $id = null)
		{
			$id = ($id == null) ? $this->id : $id; // garante que tenha um id para achar nas outras tabelas
			// Anda pelas tabelas, cujos valores também dependem dessa entidade e exclui esses valores 		
			if(!empty($this->tabelas_excluir))
			{	
				foreach($this->tabelas_excluir as $tabela => $campos)
				{
					$factory = new Factory();
					$model = $factory->cria_instancia_model($tabela); // crio o model de outra tabela
					
					// se for um array, exclui o elemento baseado em todos os ids iguais ao id do elemento
					if(is_array($campos))
					{
						foreach($campos as $campo)
						{
							$campo_ids = $model->sqlController->get_elements('id', null, array($campo => $id));
							// Antes de excluir os registros de outra tabela, excluo os registros que dependem desses registros da outra tabela
							foreach($campo_ids as $campo_id) $model->excluir_relacionamentos($session, $campo_id);
						}
						// Excluo os registros que dependem dessa entidade
						if(!$model->sqlController->delete_row($id, $tabela, $campos))
						{
							$session->set_session_msg(ERRO,"Erro ao excluir os registros da tabela $tabela do banco de dados");
						}
					}
					else
					{
						// $campos vale o nome do campo para comparar com o primeiro valor $id
						$campo_ids = $model->sqlController->get_elements('id', null, array($campos => $id));
						// Antes de excluir os registros de outra tabela, excluo os registros que dependem desses registros da outra tabela
						foreach($campo_ids as $campo_id) $model->excluir_relacionamentos($session, $campo_id);
						// Exclui os registros que dependem dessa entidade
						if(!$model->sqlController->delete_row($id, $tabela, $campos))
						{
							$session->set_session_msg(ERRO,"Erro ao excluir os registros da tabela $tabela do banco de dados");
						}
					}
				}
			}
		}
		
		// Exclui uma linha do banco de dados, retornando para a página em $retorno, passando para a SESSION o valor de $mensagem_sucesso
		public function excluir($id = null)
		{
			$id = ($id == null) ? $this->id : $id; // Se id for null pega o id que está no model, se não é para usar o id passado
			
			$this->excluir_relacionamentos(&$session, $id); // exclui o elemento nas tabelas de relacionamento
			
			return $this->sqlController->delete_row($id);
		}
		
		// Copia um array de dados para o array $this->dados
		public function set_dados($dados)
		{
			$this->dados = $dados;
		}
		
		// Overloading
		
		// Se não existe na entidade, atribui no array de dados
		public function __set($name, $value) 
		{
	        $this->dados[$name] = $value;
	    }
	    
	    // Retorna o valor passado do array de dados passado, tentando colocar um sufixo ou tentando tirar o sufixo
	    protected function ret_valor_array($dados, $name)
	    {
	    	// Se existe o nome passado, retorna o seu valor
	        if (array_key_exists($name, $dados)) 
	        {
	            return $dados[$name];
	        }
	        // Se existe o nome passado com sufixo, retorna o seu valor
	        else if (array_key_exists($name.$this->sufixo, $dados)) 
	        {
	            return $dados[$name.$this->sufixo];
	        }
	        // Se existe o nome passado, porém sem o sufixo, retorna o seu valor
	    	else 
	        {
	        	$name_sem_sufixo = str_replace($this->sufixo, '', $name);
	        	if (array_key_exists($name_sem_sufixo, $dados))
	        	{
	            	return $dados[$name_sem_sufixo];
	        	}
	        }
	        return null;
	    }
	    
		// Se existe na entidade, retorna o valor, pegando do array de dados
	    public function __get($name) 
	    {
	    	$valor = $this->ret_valor_array($this->dados, $name); // Pega o valor do nome no array interno de dados  	
	    	return $valor; // Retorna esse valor
	    }
	
	    // Verifica se a variável existe na entidade
	    public function __isset($name) 
	    {
	    	return isset($this->$name);
	    }
	    
	    // Retira o valor da entidade	
	    public function __unset($name) 
	    {
	    	// Faço unset do nome passado, tentando tirar o sufixo, tentando colocar o nome puro, e tentando colocar o sufixo no nome
	        unset($this->dados[$name]);
	        unset($this->dados[$name.$this->sufixo]);
	        
	        $name_sem_sufixo = str_replace($this->sufixo  , '', $name);
	    	unset($this->dados[$name_sem_sufixo]);
	    }
	    
	}
?>